home *** CD-ROM | disk | FTP | other *** search
/ Network Supervisor's Toolkit / Network Supervisor's Toolkit.iso / perform / netframe / dx.c next >
C/C++ Source or Header  |  1996-07-10  |  20KB  |  707 lines

  1. /*
  2. $Module DX.C$
  3.  
  4. Copyright 1990
  5. By NetFRAME Systems Inc.
  6.    Sunnyvale, California U.S.A.
  7.  
  8. $Author:   Karl S. Johnson  $
  9. $Date:   02 Feb 1990 15:46:42  $
  10. $Revision:   1.2  $
  11.  
  12. $Description$
  13. This is a NetWare Loadable Module to measure disk performance.
  14. $EndDescription$
  15.  
  16.  
  17.    Revision History
  18. $Log:   H:/386/NLMS/DX/SRC/VCS/DX.C  $
  19.  * 
  20.  *    Rev 1.2   02 Feb 1990 15:46:42   Karl S. Johnson
  21.  * Changed to use PDiskRequest to allow greater than cache block size I/Os.
  22.  * 
  23.  *    Rev 1.1   01 Feb 1990 14:47:18   Karl S. Johnson
  24.  * Added full random I/O. Added paged display and processor utilization.
  25.  * 
  26.  *    Rev 1.0   01 Feb 1990 10:03:50   Karl S. Johnson
  27.  * Initial revision.
  28. */
  29. #include "procdefs.h"
  30. #include "dmanage.h"
  31. #include "random.h"
  32. #define DX_PRIORITY 50
  33. #define MAX_DISKS 64
  34. #define MAX_REQUESTS_PER_DISK 64
  35. #define MAX_KB_PER_IO 60
  36. #define DISKS_PER_PAGE 16
  37. #define STACK_SIZE 2048*16
  38. #define _UNUSED(x) if (0) if (x)
  39. extern LONG NumberOfPollingLoops, MaximumNumberOfPollingLoops;
  40. BYTE *DXRequestArea;
  41. LONG DXRequestAreaSize;
  42. struct rstruct *DXRequestFreeListHead;
  43. struct rstruct *DXRequestFreeListTail;
  44. LONG DXUpdateInterval;
  45. LONG DXKBytesPerIO;
  46. LONG DXMaxIOsPerDrive;
  47. BYTE DXReadWrite;
  48. BYTE DXAccessType;
  49. ScreenStruct *dXerciseScreen;
  50. int DriveCount;
  51. BYTE *stack;            /* DX Monitor Stack */
  52. LONG stackSize;
  53. BYTE *DXStack = NULL;        /* DX Exerciser Stack */
  54. LONG DXActualStackSize;
  55. LONG dXerciseModuleHandle;
  56. LONG ServerProcessPriority = 50;
  57. LONG DXMonitorProcessID = 0;
  58. LONG DXerciseProcessID = 0;
  59. int DXWakeRequested = 0;
  60. char DXMonitorStatus = 'I'; /* Possible Status I=Initializing R=Running P=Pending Stop S=Stopped */
  61. struct DiskData
  62.     {
  63.     struct ldstruct *LDisk;
  64.     struct plock *Lock;
  65.     int OutstandingIOs;
  66.     LONG Size;
  67.     LONG Sector;
  68.     LONG Random;
  69.     BYTE Name[80];
  70.     } Disk[MAX_DISKS];
  71. struct PerformanceData
  72.     {
  73.     LONG TotalIOsCompleted;
  74.     LONG CurrentIOsCompleted;
  75.     LONG TotalErrors;
  76.     } Performance[MAX_DISKS];
  77. struct PerformanceData Aggregate;
  78. int AggregateOutstandingIOs = 0;
  79. BYTE CommonBuffer[1024*MAX_KB_PER_IO];
  80.  
  81. void
  82. LockAlert( LONG skipParameter, LONG criticality, int DriveIndex );
  83.  
  84. void
  85. DXIOCompletion( struct rstruct *currentRequest );
  86.  
  87. struct rstruct *DXAllocateRequest();
  88.  
  89. void
  90. DXReturnRequest( struct rstruct *Request);
  91.  
  92. void
  93. MakeDriveDescription( struct pdstruct *driveID, BYTE *buffer);
  94.  
  95. void
  96. DXercise();
  97.  
  98. void
  99. DXMonitor();
  100.  
  101. LONG
  102. StartProcedure(
  103.         LONG moduleHandle,
  104.         ScreenStruct *initializationErrorScreen,
  105.         BYTE *commandLine,
  106.         BYTE *loadDirectoryPath,
  107.         LONG unitializedDataLength,
  108.         LONG fileHandle,
  109.         LONG (*ReadRoutine)(LONG handle, LONG offset, BYTE *buffer, LONG length),
  110.         LONG customDataOffset,
  111.         LONG customDataSize)
  112. {
  113.     static BYTE dXerciseScreenName[] = "DX Screen";
  114.     BYTE *Token;
  115.     _UNUSED(commandLine);
  116.     _UNUSED(loadDirectoryPath);
  117.     _UNUSED(unitializedDataLength);
  118.     _UNUSED(fileHandle);
  119.     _UNUSED(ReadRoutine);
  120.     _UNUSED(customDataOffset);
  121.     _UNUSED(customDataSize);
  122.  
  123.     for (Token = commandLine; *Token != '\000'; Token++)
  124.         {
  125.         if (*Token == 'D') EnterDebugger();
  126.         }
  127.  
  128.     stack = GetNonMovableMemory(STACK_SIZE, &stackSize);
  129.     if (stack == NULL)
  130.         {
  131.         OutputToScreen(initializationErrorScreen,
  132.             "DX: Unable to get memory for stack\r\n");
  133.         goto Error0;
  134.         }
  135.  
  136.     dXerciseModuleHandle = moduleHandle;
  137.     if (OpenScreen(dXerciseScreenName, &dXerciseScreen) != 0)
  138.         {
  139.         OutputToScreen(initializationErrorScreen,
  140.             "DX: Unable to open DX screen\r\n");
  141.         goto Error1;
  142.         }
  143.  
  144.     /* The variable stackSize contains the amount of memory actually
  145.        allocated for the stack, so use it instead of STACK_SIZE.*/
  146.  
  147.     /* this should be the last thing we do */
  148.     DXMonitorProcessID = CCreateProcess(DX_PRIORITY, DXMonitor,
  149.             stack + stackSize, stackSize, "DXercise");
  150.     while ( DXMonitorStatus == 'I') CRescheduleLast();
  151.     switch ( DXMonitorStatus)
  152.         {
  153.         case 'R':
  154.         break;
  155.         case 'S':
  156.         default:
  157.         OutputToScreen( systemConsoleScreen,
  158.                 "Failed to start DX monitor process\r\n" );
  159.         goto Error3;
  160.         }
  161.     return (0);
  162.  
  163. /* Error Recovery */
  164. Error3:
  165.     CloseScreen(dXerciseScreen);
  166. Error1:
  167.     ReturnNonMovableMemory(stack);
  168. Error0:
  169.     return (-1);
  170. }
  171.  
  172. void ExitProcedure(void)
  173. {
  174.     int i;
  175.     if ( DXMonitorProcessID != 0 ) CDestroyProcess( DXMonitorProcessID );
  176.  
  177.     if ( DXMonitorStatus != 'S' ) /* if not already stopped - stop */
  178.         {
  179.         DXMonitorStatus = 'S';
  180.         while ( AggregateOutstandingIOs != 0 ) CRescheduleLast();
  181.         if ( DXerciseProcessID != 0 ) CDestroyProcess( DXerciseProcessID );
  182.         }
  183.     for ( i = 0; i < DriveCount; i++ ) ReleasePartition( Disk[i].Lock );
  184.     CloseScreen( dXerciseScreen );
  185.     if ( DXStack != NULL ) ReturnNonMovableMemory( DXStack );
  186.     if ( DXRequestArea != NULL ) ReturnNonMovableMemory( DXRequestArea );
  187.  
  188.     ReturnNonMovableMemory( stack );
  189.  
  190. }
  191.  
  192. void DXExit(void)
  193. {
  194.     KillMe((struct LoadDefinitionStructure *)dXerciseModuleHandle);
  195.  
  196.     /* Sleep forever until the exit procedure kills this process */
  197.     for (;;)
  198.         CSleepUntilInterrupt();
  199. }
  200.  
  201. void
  202. DXMonitor()
  203. {
  204.     struct pdstruct *driveID;
  205.     struct ldstruct *lDisk;
  206.     long int LStatus;
  207.     int DriveIndex;
  208.     LONG MaxPage;
  209.     LONG CurrentPage;
  210.     LONG FirstDrive;
  211.     LONG LastDrive;
  212.     LONG StartTime;
  213.     LONG IntervalStartTime;
  214.     LONG ElapsedSeconds;
  215.     LONG IntervalSeconds;
  216.     LONG Tenths;
  217.     LONG utilization;
  218.     char *AccessString;
  219.     char *IOTypeString;
  220.     struct rstruct *Request;
  221.     LONG UpdateTicks;
  222.     int i;
  223.     BYTE Answer;
  224.     BYTE Dummy;
  225.     BYTE buffer[200];
  226.  
  227.     RandomSectorsCount = sizeof( RandomSectors ) / 4;
  228.     /* Activate our screen */
  229.     Enable();
  230.     ActivateScreen(dXerciseScreen);
  231.  
  232.     /* Tell DXLoad we are Running */
  233.     DXMonitorStatus = 'R';
  234.     /* Ask for type of disk drive access S=Sequential R=Random*/
  235.     InputFromScreen( dXerciseScreen,
  236.             "FSR",
  237.             2,
  238.             2,
  239.             buffer,
  240.             0L,
  241.             TRUE,
  242.             "S",
  243.             "Exerciser access pattern [S=Sequential R=Random]? " );
  244.     DXAccessType = buffer[0];
  245.     switch ( DXAccessType )
  246.     {
  247.     case 'F':
  248.     AccessString = "Fixed";
  249.     break;
  250.     case 'S':
  251.     AccessString = "Sequential";
  252.     break;
  253.     case 'R':
  254.     AccessString = "Random";
  255.     break;
  256.     default:
  257.     AccessString = "UnknownTest";
  258.     }
  259.     /* Ask for Read or Write test */
  260.     InputFromScreen( dXerciseScreen,
  261.             "RW",
  262.             2,
  263.             2,
  264.             buffer,
  265.             0L,
  266.             TRUE,
  267.             "R",
  268.             "Exerciser test type [R=Read W=Write]? " );
  269.     DXReadWrite = buffer[0];
  270.     switch ( DXReadWrite )
  271.     {
  272.     case 'R':
  273.     IOTypeString = "Read";
  274.     break;
  275.     case 'W':
  276.     IOTypeString = "Write";
  277.     break;
  278.     default:
  279.     IOTypeString = "UnknownIO";
  280.     }
  281.  
  282.     /* Ask for block size */
  283.     PromptForUnsignedNumber( dXerciseScreen,
  284.                  &DXKBytesPerIO,
  285.                  1L,
  286.                  MAX_KB_PER_IO,
  287.                  10L,
  288.                  0L,
  289.                  TRUE,
  290.                  8L,
  291.                  "Number of kilo bytes (KB) per IO [1-%d]? ",
  292.                              MAX_KB_PER_IO);
  293.  
  294.     /* Ask for queue depth */
  295.     PromptForUnsignedNumber( dXerciseScreen,
  296.                  &DXMaxIOsPerDrive,
  297.                  1L,
  298.                  MAX_REQUESTS_PER_DISK,
  299.                  10L,
  300.                  0L,
  301.                  TRUE,
  302.                  3L,
  303.                  "Number of concurrent IO to queue per drive [1-%d]? ",
  304.                              MAX_REQUESTS_PER_DISK);
  305.  
  306.     /* Ask for update interval */
  307.     PromptForUnsignedNumber( dXerciseScreen,
  308.                  &DXUpdateInterval,
  309.                  1L,
  310.                  60L,
  311.                  10L,
  312.                  0L,
  313.                  TRUE,
  314.                  5L,
  315.                  "Screen update interval in seconds [1-60]? ");
  316.     ConvertSecondsToTicks( DXUpdateInterval, 0L, &UpdateTicks );
  317.  
  318.     /* List all possible disks */
  319.     ScanForNewDrives();
  320.  
  321.     /* Ask for selections or go */
  322.     DriveCount = 0;
  323.     for (driveID = PhysicalDiskList; driveID != NULL; driveID =
  324.                 driveID->PPLink)
  325.     {
  326.     for ( i = 0; i < sizeof( Disk[0].Name ); i++ ) Disk[DriveCount].Name[i] = 0;
  327.     MakeDriveDescription( driveID, Disk[DriveCount].Name );
  328.     LStatus = PromptForYesOrNo( dXerciseScreen,
  329.                    0L,
  330.                    TRUE,
  331.                    "\r\nExercise %s ?",
  332.                    Disk[DriveCount].Name );
  333.     if ( LStatus )
  334.         {
  335.         for ( lDisk = driveID->PLogicalLink;
  336.           lDisk != NULL;
  337.           lDisk = lDisk->LLogicalLink )
  338.         if ( lDisk->LOSType == NETWAREOSTYPE )
  339.             break;
  340.         if ( lDisk == NULL )
  341.         {
  342.         OutputToScreen( dXerciseScreen,
  343.         "\r\nDisk does not have a NetWare Partition - not usable\r\n");
  344.         break;
  345.         }
  346.         Disk[DriveCount].Lock = LockSinglePartition(
  347.                 lDisk,
  348.                 LockAlert,
  349.                 DriveCount,
  350.                 EXCLUSIVELOCK,
  351.                 (BYTE *)"DXercise" );
  352.         if ( Disk[DriveCount].Lock == NULL )
  353.         {
  354.         OutputToScreen( dXerciseScreen,
  355.         "\r\nCannot get EXCLUSIVE LOCK on NetWare partition - not usable\r\n");
  356.         break;
  357.         }
  358.         Disk[DriveCount].LDisk = lDisk;
  359.         Disk[DriveCount].Size = lDisk->LLogicalSize;
  360.         Disk[DriveCount].OutstandingIOs = 0;
  361.         Performance[DriveCount].TotalIOsCompleted = 0;
  362.         Performance[DriveCount].CurrentIOsCompleted = 0;
  363.         Performance[DriveCount].TotalErrors = 0;
  364.         DriveCount++;
  365.         }
  366.     }
  367.     /* Zero aggregate preformance numbers */
  368.  
  369.     Aggregate.TotalIOsCompleted = 0;
  370.     Aggregate.CurrentIOsCompleted = 0;
  371.     Aggregate.TotalErrors = 0;
  372.     AggregateOutstandingIOs = 0;
  373.  
  374.     /* Make sure at least one disk is selected */
  375.  
  376.     if ( DriveCount < 1 )
  377.     {
  378.     OutputToScreen( dXerciseScreen,
  379.             "No Drives selected\r\n" );
  380.     Delay( 91 );
  381.     DXExit();
  382.     }
  383.     /* Allocate and initialize the request list */
  384.     DXRequestAreaSize = DriveCount
  385.                          * MAX_REQUESTS_PER_DISK
  386.                          * ( sizeof( struct rstruct ) + 4 );
  387.     DXRequestArea = GetNonMovableMemory( DXRequestAreaSize,
  388.                         &DXRequestAreaSize );
  389.     if ( DXRequestArea == NULL)
  390.     {
  391.     OutputToScreen ( dXerciseScreen,
  392.             "Insufficient memory to allocate disk requests\r\n");
  393.     Delay( 91 );
  394.     DXExit();
  395.     }
  396.     CSetB( 0, DXRequestArea, DXRequestAreaSize );
  397.     DXRequestFreeListHead = NULL;
  398.     DXRequestFreeListTail = NULL;
  399.     Request = (struct rstruct *)DXRequestArea;
  400.     for ( i = 0; i < DXRequestAreaSize / ( sizeof( struct rstruct ) + 4 ); i++ )
  401.         {
  402.         *(LONG *)Request = 0x55514552;      /* Put in the "REQU" block signature */
  403.         Request = (struct rstruct *)((LONG *)Request + 1); /* just before block */
  404.         DXReturnRequest( Request );
  405.         Request++;
  406.         }
  407.  
  408.     /* Start DXercise */
  409.     DXStack = GetNonMovableMemory( STACK_SIZE,
  410.                         &DXActualStackSize );
  411.     if ( DXStack == NULL)
  412.     {
  413.     OutputToScreen ( dXerciseScreen,
  414.             "Insufficient memory to start DX subprocess\r\n");
  415.     Delay( 91 );
  416.     DXExit();
  417.     }
  418.     DXerciseProcessID = CCreateProcess( DX_PRIORITY,
  419.                     DXercise,
  420.                     DXStack + DXActualStackSize,
  421.                     DXActualStackSize,
  422.                     "DXercise");
  423.     /* Compute pages of displays */
  424.     MaxPage = ( DriveCount / DISKS_PER_PAGE ) + 1;
  425.     CurrentPage = 1;
  426.     /* Clear the Screen and setup the title */
  427.     ClearScreen( dXerciseScreen );
  428.     PositionOutputCursor( dXerciseScreen, 0, 0 );
  429.     OutputToScreen( dXerciseScreen,
  430.         "NetFRAME Disk Test : %s %s %d KB/IO with %d IOs queued  Page %d of %d",
  431.         AccessString,
  432.         IOTypeString,
  433.         DXKBytesPerIO,
  434.         DXMaxIOsPerDrive,
  435.                 CurrentPage,
  436.                 MaxPage );
  437.  
  438.     /* Note the starting time for later computations */
  439.     StartTime = CurrentTime;
  440.  
  441.     while (1)
  442.     {
  443.     /* Sleep for update interval */
  444.         IntervalStartTime = CurrentTime;
  445.     Delay( UpdateTicks );
  446.     /* Test for keyboard key */
  447.     if ( CheckKeyStatus( dXerciseScreen ) )
  448.         {
  449.         /* Get the key and process it */
  450.         GetKey( dXerciseScreen, &Dummy, &Answer, &Dummy, &Dummy, 0L );
  451.             if ( Answer == 'Q' || Answer =='q' )
  452.                 {
  453.             PositionOutputCursor( dXerciseScreen, 24, 0 );
  454.             LStatus = PromptForYesOrNo( dXerciseScreen,
  455.                    0L,
  456.                    TRUE,
  457.                    "Exit Disk Exerciser? " );
  458.             if ( LStatus )
  459.             {
  460.             DXMonitorStatus = 'P';
  461.             }
  462.                 }
  463.             else
  464.                 {
  465.                 if ( Answer > '0' && Answer <= '9' )
  466.                     {
  467.                     i = Answer - '0';
  468.                     if ( i <= MaxPage )
  469.                         {
  470.                         CurrentPage = i;
  471.                         ClearScreen( dXerciseScreen );
  472.                         PositionOutputCursor( dXerciseScreen, 0, 0 );
  473.                         OutputToScreen( dXerciseScreen,
  474.                         "NetFRAME Disk Test : %s %s %d KB/IO with %d IOs queued  Page %d of %d",
  475.                         AccessString,
  476.                         IOTypeString,
  477.                         DXKBytesPerIO,
  478.                         DXMaxIOsPerDrive,
  479.                                 CurrentPage,
  480.                                 MaxPage );
  481.                         }
  482.                     else RingTheBell();
  483.                     }
  484.                 else RingTheBell();
  485.                 }    
  486.         }
  487.     /* Test for requested stop */
  488.     if ( DXMonitorStatus == 'P' )
  489.         {
  490.         while ( AggregateOutstandingIOs != 0 ) CRescheduleLast();
  491.         if ( DXerciseProcessID != 0 )
  492.             {
  493.         CDestroyProcess( DXerciseProcessID );
  494.             DXerciseProcessID = 0;
  495.         }
  496.         DXMonitorStatus = 'S';      /* Say we are stopped */
  497.         ReturnNonMovableMemory( DXStack );
  498.         DXStack = NULL;
  499.         DXExit();
  500.         }
  501.     /* Compute and display the numbers */
  502.     ConvertTicksToSeconds( ( CurrentTime - StartTime ),
  503.                    &ElapsedSeconds,
  504.                    &Tenths );
  505.     ConvertTicksToSeconds( ( CurrentTime - IntervalStartTime ),
  506.                    &IntervalSeconds,
  507.                    &Tenths );
  508.  
  509.     PositionOutputCursor( dXerciseScreen, 1, 0 );
  510.     OutputToScreen( dXerciseScreen, "%-28.28s ","    Disk" );
  511.     OutputToScreen( dXerciseScreen, "%-9s ","  KB/sec." );
  512.     OutputToScreen( dXerciseScreen, "%-9s "," IOs/sec." );
  513.     OutputToScreen( dXerciseScreen, "%-9s ","Ave. KB/s" );
  514.     OutputToScreen( dXerciseScreen, "%-9s ","Total IOs" );
  515.     OutputToScreen( dXerciseScreen, "%-5s\r\n","Error" );
  516.         FirstDrive = ( CurrentPage - 1 ) * DISKS_PER_PAGE;
  517.         /* note - LastDrive is really last drive index (0 based) plus 1) */
  518.         if ( CurrentPage == MaxPage ) 
  519.             {
  520.             LastDrive = DriveCount;
  521.             }
  522.         else
  523.             {
  524.             LastDrive = CurrentPage * DISKS_PER_PAGE;
  525.             }
  526.     for ( DriveIndex = FirstDrive; DriveIndex < LastDrive; DriveIndex++ )
  527.         {
  528.         OutputToScreen( dXerciseScreen, "%-28.28s ", Disk[DriveIndex].Name );
  529.         OutputToScreen( dXerciseScreen, "%9d ", ( Performance[DriveIndex].CurrentIOsCompleted * DXKBytesPerIO ) / IntervalSeconds );
  530.         OutputToScreen( dXerciseScreen, "%9d ", Performance[DriveIndex].CurrentIOsCompleted / DXUpdateInterval );
  531.         OutputToScreen( dXerciseScreen, "%9d ", ( Performance[DriveIndex].TotalIOsCompleted * DXKBytesPerIO ) / ElapsedSeconds );
  532.         OutputToScreen( dXerciseScreen, "%9d ", Performance[DriveIndex].TotalIOsCompleted );
  533.         OutputToScreen( dXerciseScreen, "%5d ", Performance[DriveIndex].TotalErrors );
  534.         OutputToScreen( dXerciseScreen, "\r\n" );
  535.         }
  536.             
  537.         /* Aggregate numbers */
  538.     OutputToScreen( dXerciseScreen, "\r\n" );
  539.     OutputToScreen( dXerciseScreen, "%-28.28s ", "Aggregate Disk Performance" );
  540.     OutputToScreen( dXerciseScreen, "%9d ", ( Aggregate.CurrentIOsCompleted * DXKBytesPerIO ) / IntervalSeconds );
  541.     OutputToScreen( dXerciseScreen, "%9d ", Aggregate.CurrentIOsCompleted / DXUpdateInterval );
  542.     OutputToScreen( dXerciseScreen, "%9d ", ( Aggregate.TotalIOsCompleted * DXKBytesPerIO ) / ElapsedSeconds );
  543.     OutputToScreen( dXerciseScreen,"%9d ", Aggregate.TotalIOsCompleted );
  544.     OutputToScreen( dXerciseScreen,"%5d ", Aggregate.TotalErrors );
  545.     OutputToScreen( dXerciseScreen, "\r\n" );
  546.  
  547.     /* Utilization */
  548.     utilization = 100 - ((NumberOfPollingLoops * 100 +
  549.             (MaximumNumberOfPollingLoops >> 1)) / MaximumNumberOfPollingLoops);
  550.     OutputToScreen( dXerciseScreen, "Server utilization %6d%%\r\n", utilization);
  551.  
  552.         /* Instructions */
  553.     OutputToScreen( dXerciseScreen, "Q to quit or page number to view\n\r" );
  554.  
  555.         /* Zero current counts */
  556.     for ( DriveIndex = 0; DriveIndex < DriveCount; DriveIndex++ )
  557.         {
  558.         Performance[DriveIndex].CurrentIOsCompleted = 0;
  559.             }
  560.     Aggregate.CurrentIOsCompleted = 0;
  561.  
  562.     }
  563. }
  564.  
  565. void
  566. DXercise()
  567. {
  568.     struct rstruct *Request;
  569.     int i;
  570.     while (1)
  571.     {
  572.     if ( DXMonitorStatus != 'R' )   /* If not running go to sleep forever */
  573.                     /* and wait to be destroyed */
  574.         {
  575.         while (1) CSleepUntilInterrupt();
  576.         }
  577.     for ( i = 0; i < DriveCount; i++ )
  578.         {
  579.         while ( Disk[i].OutstandingIOs < DXMaxIOsPerDrive )
  580.         {
  581.         /* Start another IO */
  582.         Disk[i].OutstandingIOs++;
  583.         switch ( DXAccessType )
  584.             {
  585.             case 'F':
  586.  
  587.             Disk[i].Sector = 0;
  588.             break;
  589.  
  590.             case 'S':
  591.  
  592.             Disk[i].Sector += DXKBytesPerIO * 2;
  593.             if ( Disk[i].Sector + ( DXKBytesPerIO * 2 ) >=
  594.              Disk[i].Size ) Disk[i].Sector = 0;
  595.             break;
  596.  
  597.             case 'R':
  598.  
  599.             Disk[i].Sector = RandomSectors[Disk[i].Random] % ( Disk[i].Size - ( DXKBytesPerIO * 2 ) );
  600.             Disk[i].Random++;
  601.             if ( Disk[i].Random == RandomSectorsCount )
  602.                         Disk[i].Random = 0;
  603.             break;
  604.             }
  605.         /* Build a physical I/O request */
  606.                 Request = DXAllocateRequest();
  607.         Request->RReserved = (LONG)Disk[i].LDisk;
  608.                 Request->RDiskID = Disk[i].LDisk->LPLink;
  609.                 Request->RStartingSector = Disk[i].Sector +
  610.             Disk[i].LDisk->LPartitionOffset;
  611.         Request->RMirrorLink = Request;
  612.                 Request->RRoutine = DXIOCompletion;
  613.                 Request->REBXValue = i;
  614.                 Request->RNumberOfSectors = DXKBytesPerIO * 2;
  615.                 Request->RBufferAddress = CommonBuffer;
  616.         if ( DXReadWrite == 'W' )
  617.                     Request->RFunction = 1;
  618.         else
  619.                     Request->RFunction = 0;
  620.                 PDiskRequest( Request );
  621.         AggregateOutstandingIOs++;
  622.         }
  623.         }
  624.     DXWakeRequested = 0;
  625.     CSleepUntilInterrupt();
  626.     }
  627. }
  628.  
  629. void
  630. LockAlert( LONG skipParameter, LONG criticality, int DriveIndex )
  631. {
  632.     _UNUSED( skipParameter );
  633.     _UNUSED( criticality );
  634.     _UNUSED( DriveIndex );
  635. }
  636.  
  637. void
  638. DXIOCompletion( struct rstruct *currentRequest )
  639. {
  640.     int i;
  641.     i = (int)currentRequest->REBXValue;
  642.     --Disk[i].OutstandingIOs;
  643.     --AggregateOutstandingIOs;
  644.     /* Update global aggregate counts */
  645.     Aggregate.TotalIOsCompleted++;
  646.     Aggregate.CurrentIOsCompleted++;
  647.     /* Update individual disk counts */
  648.     Performance[i].TotalIOsCompleted++;
  649.     Performance[i].CurrentIOsCompleted++;
  650.     /* Count errors */
  651.     if ( currentRequest-> RCompletionCode != 0 )
  652.     {
  653.     Performance[i].TotalErrors++;
  654.     Aggregate.TotalErrors++;
  655.     }
  656.     DXReturnRequest( currentRequest );
  657.     if ( !DXWakeRequested )
  658.     {
  659.     DXWakeRequested = -1;
  660.     CRescheduleFromInterrupt( DXerciseProcessID );
  661.     }
  662. }
  663.  
  664. struct rstruct *DXAllocateRequest()
  665. {
  666.     struct rstruct *Request;
  667.     if ( DXRequestFreeListHead == NULL ) Abend( "DX Out of request nodes");
  668.     Request = DXRequestFreeListHead;
  669.     DXRequestFreeListHead = Request->RLink;
  670.     return ( Request );
  671. }
  672.  
  673. void
  674. DXReturnRequest( struct rstruct *Request)
  675. {
  676.     if ( DXRequestFreeListHead == NULL )
  677.         {
  678.         DXRequestFreeListHead = Request;
  679.         DXRequestFreeListTail = Request;
  680.         }
  681.     else
  682.         {
  683.         DXRequestFreeListTail->RLink = Request;
  684.         DXRequestFreeListTail = Request;
  685.         }
  686.     DXRequestFreeListTail->RLink = NULL;
  687. }
  688.  
  689. void
  690. MakeDriveDescription(
  691.     struct pdstruct *driveID,
  692.     BYTE *buffer)
  693. {
  694.     while (*buffer != 0)
  695.         ++buffer;
  696.     sprintf(buffer, (BYTE *)"%15.15s ", driveID->PDeviceName + 1);
  697.     while (*buffer != 0)
  698.         ++buffer;
  699.     sprintf(buffer, (BYTE *)"%d-", driveID->PCardNumber);
  700.     while (*buffer != 0)
  701.         ++buffer;
  702.     sprintf(buffer, (BYTE *)"%d-", driveID->PControllerNumber);
  703.     while (*buffer != 0)
  704.         ++buffer;
  705.     sprintf(buffer, (BYTE *)"%d", driveID->PDriveNumber);
  706. }
  707.